package com.asiainfo.servlet;

import com.asiainfo.annotation.Autowired;
import com.asiainfo.annotation.Controller;
import com.asiainfo.annotation.RequestMapping;
import com.asiainfo.annotation.Service;
import com.asiainfo.controller.ErrorController;
import com.asiainfo.handlerAdapter.HandlerAdapterService;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/** 第一步:把所有的bean扫描出来  扫描所有的class文件
 *  第二步:根据集合里的class类创建对象
 *  第三步:根据bean进行关系依赖处理
 *  第四步:建立完整映射关系
 *  第五步:根据客户端请求，在doPost方法里获取到客户端请求的路径，然后在装载好的容器中进行获取对应实例对象。
 *
 *
 *  MVC核心类
 */
public class DispatcherServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private List<String> classNames = new ArrayList<String>(); //所有类的路径
    private Map<String,Object> beans = new HashMap<String,Object>();
    private Map<String,Object> handlerMap = new HashMap<String, Object>();
    private static final String  errorPath="/ym/error/error";  //错误页面路径
    public void init()
    {
        /*扫描*/
        this.scanPackage("com.asiainfo");
        /*创建对象*/
        this.doInstance();
        /*根据bean进行关系依赖*/
        this.doIoc();
        /*requestMaping完整映射*/
        this.buildUrlMapping();
    }

    @Override
    public void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
    {
        this.doPost(httpServletRequest,httpServletResponse);
    }

    @Override
    public void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
    {
        //获得请求路径
        try {
            String url = httpServletRequest.getRequestURI();
            if(url.equals("/favicon.ico"))  //防止自动加载ico的干扰
                return;
            String context = httpServletRequest.getContextPath();
            String path = url.replace(context, "");
            System.out.println("path:"+path);
            System.out.println(path.split("/"));
            //根据路径找到处理方法
            Method method = (Method) handlerMap.get(path);
            //处理路径获得bean
            String[] paths = path.split("/");
            String beanPath = "";
            for(int i=1;i<paths.length-1;i++)  // 和Controller上的路径对应
            {
                beanPath=beanPath+"/"+paths[i];
            }
            Object obj = (Object) beans.get(beanPath);  //获得bean
            HandlerAdapterService ha = (HandlerAdapterService) beans.get("handlerAdapter");
            Object[] args = ha.handle(httpServletRequest, httpServletResponse, method, beans);
            try{
                //反射实现功能
                method.invoke(obj,args);
            } catch (Exception e) {
                System.out.println("invoke Eoorr：go to error！");
                this.goToError(httpServletRequest,httpServletResponse);
            }
        } catch (Exception e) {
            try {
                ((Method)handlerMap.get(errorPath)).invoke((ErrorController) beans.get("/ym/error"),new Object[]{httpServletRequest,httpServletResponse});
            } catch (Exception ex) {
                System.out.println("path error：go to error!");
                return;
            }
        }

    }

    /**
     * 扫描class文件，放入className里面
     * 扫描是 com.asiainfo...
     * url是: com/asiainfo...
     * @param basePack
     */
    private void scanPackage(String basePack)
    {
        URL url = this.getClass().getClassLoader().getResource("/"+basePack.replaceAll("\\.","/"));
        String fileStr = url.getFile();
        File file = new File(fileStr);
        String fileArray[] = file.list();
        for(String path:fileArray)
        {
            File filePath = new File(fileStr+path);
            if(filePath.isDirectory()) //文件夹，继续递归
            {
                scanPackage(basePack+"."+path);
            }
            else
            {
                //找到class文件，放入集合
                classNames.add(basePack+"."+filePath.getName());
            }
        }

    }

    /**
     * 将扫描到的beans实例化,并保存下来
     */
    private void doInstance()
    {
        if(classNames.isEmpty())
        {
            System.out.println("Instance Error1--126");
            return;
        }
        for(String className:classNames)
        {
            String cName = className.replace(".class","");//获得文件名
            try{
                //创建类
                Class<?> newClass = Class.forName(cName);
                if(newClass.isAnnotationPresent(Controller.class))
                {
                    //控制注解
                    Object instance = newClass.newInstance(); //类加载创建类
                    RequestMapping requestMapping = newClass.getAnnotation(RequestMapping.class);
                    String value = requestMapping.value(); //请求路径
                    beans.put(value,instance); //装载到bean
                }
                else if(newClass.isAnnotationPresent(Service.class))
                {
                    //service注解
                    Service service = newClass.getAnnotation(Service.class);
                    Object instance = newClass.newInstance();
                    beans.put(service.value(),instance);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 把Service 注入到控制层
     */
    private void doIoc()
    {
        if(beans.entrySet().size()<=0)
        {
            System.out.println("没有实例化对象");
            return;
        }
        for(Map.Entry<String,Object> entry:beans.entrySet())
        {
            Object instance = entry.getValue();//获得已经实例化过的beans
            Class<?> newClass = instance.getClass();
            if(newClass.isAnnotationPresent(Controller.class))
            {
                //获得类中的所有字段
                Field[] fields = newClass.getDeclaredFields();
                for(Field field:fields)
                {
                    if(field.isAnnotationPresent(Autowired.class))
                    {
                        Autowired autowired = field.getAnnotation(Autowired.class);
                        String key = autowired.value();
                        //开启反射访问私有变量
                        field.setAccessible(true);
                        try{
                            //给字段设置值
                            field.set(instance,beans.get(key));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    /**
     * RequestMapping注解和方法上的RequestMapping建立完整映射关系
     */
    private void buildUrlMapping()
    {
        if (beans.entrySet().size() <= 0)
        {
            System.out.println("没有类的实例化....");
            return;
        }
        for(Map.Entry<String,Object> entry:beans.entrySet())
        {
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            if(clazz.isAnnotationPresent(Controller.class))
            {
                RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
                String classPath = requestMapping.value(); //获得方法的路径
                Method[] methods = clazz.getMethods(); //获得方法
                for(Method method:methods)
                {
                    //如果方法上还有Requestmaping
                    if(method.isAnnotationPresent(RequestMapping.class))
                    {
                        RequestMapping methodMapping = method.getAnnotation(RequestMapping.class);
                        String methodPath = methodMapping.value();
                        handlerMap.put(classPath+methodPath,method); //路劲和方法关联起来
                    }
                }

            }
        }

    }

    /**
     * 跳转到error页面
     * @param httpServletRequest
     * @param httpServletResponse
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public void goToError(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws InvocationTargetException, IllegalAccessException
    {
        ((Method)handlerMap.get(errorPath)).invoke((ErrorController) beans.get("/ym/error"),new Object[]{httpServletRequest,httpServletResponse});
    }


}
